package cn.tedu.charging.cost.service.impl;

import cn.tedu.charging.common.pojo.param.ProgressCostParam;
import cn.tedu.charging.common.pojo.po.ChargingCostRulePO;
import cn.tedu.charging.common.pojo.vo.ProgressCostVO;
import cn.tedu.charging.cost.repository.CostRuleRepository;
import cn.tedu.charging.cost.service.CostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;

@Slf4j
@Service
public class CostServiceImpl implements CostService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 计算价格
     * @param cost
     * @return
     */
    @Override
    public ProgressCostVO calculateCost(ProgressCostParam cost) {
        //充电度数 *  每度电价格 = 花费
        //计价规则的概念
        /**
         * 名称,   开始时间,结束时间,价格
         * 尖上午,   8,     12,   1.50
         * 尖下午,   14,    18,   1.50
         * 峰,      18,    24,   1.40
         * 平,      12,    14,   1.30
         * 谷,      0,     8,    1.00
         */
        //获取当前时间 去计价规则表 查询 当前时 匹配的时间段的 计价规则
        //方案 1 直接从数据库查询 匹配的计价规则
        // select * from charging_cost_rule
        // where start_time <= now() and end_time > now and station = cost.getStationId()
        //方案 2 查询当前站的所有计价规则  用代码来匹配
        // 2.1 select * from charging_cost_rule where station = cost.getStationId()
        // 2.2 循环计价规则列表
        // 2.3 判断当前时间是否在计价规则的时间段内
        // 2.4 匹配成功
        //推荐 方案3 计价规则 放到 Redis 缓存中 缓存预热 旁路缓存 计价规则会被高频的查询
        // 3.1 从缓存查询 当前站所有计价规则
        // 3.2 循环计价规则列表
        // 3.3 判断当前时间是否在计价规则的时间段内
        // 3.4 匹配成功

        ChargingCostRulePO chargingCostRule = getChargingCostRule(cost.getStationId(), new Date());
        if (chargingCostRule == null) {
            log.error("没有找到对应的计价规则");
        }
        //匹配的计价规则的每度电的价格
        BigDecimal powerFee = chargingCostRule.getPowerFee();
        //计算价格 计算 当前同步 充电度数的 价格
        BigDecimal onceCost = calculateOnceCost(cost.getChargingCapacity(), powerFee);
        //如何计算总价 总价 每次同步充电度数计算后的价格 累加起来
        BigDecimal totalCost = getTotalCost(cost.getOrderNo(), onceCost);
        //计算时长
        Long totalTime = getTotalTime(cost.getOrderNo());

        //返回结果
        ProgressCostVO progressCostVO = new ProgressCostVO();
        //设置总价
        progressCostVO.setTotalCost(totalCost.doubleValue());
        //设置每度电价格 是否需要精确的每度点的价格 如果跨时间段 获取的每度电价格可能不一样
        progressCostVO.setPowerFee(powerFee.doubleValue());
        //总时长
        progressCostVO.setTotalTime(totalTime);
        //返回
        return progressCostVO;
    }

    /**
     * 记录开始时间
     * 同时获取总时间
     * 1 从redis获取当前订单对应的开始时间
     * 2 如果没有开始时间 则设置开始时间
     * 3 如果有开始时间 则获取总时间 当前时间 - 开始时间
     *
     * @param orderNo
     * @return
     */
    private Long getTotalTime(String orderNo){
        String key = "charging_start_time_" + orderNo;
        ValueOperations<String,Long> valueOperations = redisTemplate.opsForValue();
        Long time = valueOperations.get(key);
        if (time == null) {
            time = System.currentTimeMillis();
            valueOperations.set(key,time);
        }
        return System.currentTimeMillis() - time;
    }

    /**
     *
     * @param orderNo
     * @param onceCost
     * @return
     * INCRBY key increment
     *
     * 127.0.0.1:6379> INCRBY 1 1
     * (integer) 1 返回值
     * 127.0.0.1:6379> INCRBY 1 2
     * (integer) 3 返回值
     */
    private BigDecimal getTotalCost(String orderNo, BigDecimal onceCost) {
        String key = "charging_cost_" + orderNo;
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //INCRBY key increment
        Double increment = valueOperations.increment(key, onceCost.doubleValue());
        return new BigDecimal(increment).setScale(2, BigDecimal.ROUND_HALF_UP);
    }



    @Autowired
    private CostRuleRepository costRuleRepository;

    /**
     * 获取计价规则
     * @param stationId
     * @param now
     * @return
     */
    private ChargingCostRulePO getChargingCostRule(Integer stationId, Date now) {
        //todo 参考旁路缓存 把数据库的数据放到缓存 下次查询的时候 从缓存获取
        return costRuleRepository.getCostRule(stationId, now.getHours());
    }


    /**
     * 充电度数  *  每度电价格
     * 计算一次价格
     */
    private BigDecimal calculateOnceCost(Double chargingCapacity, BigDecimal powerFee) {
        //BigDecimal 方便计算
        // add
        // multiply
        // divide
        // subtract
        BigDecimal multiply =
                powerFee.multiply(new BigDecimal(chargingCapacity))
                        .setScale(2, BigDecimal.ROUND_HALF_UP);
        return multiply;
    }

}
